#include "../pch.h"
#include "REFEncoding.h"
#include "REFUTF32Encoding.h"
#include "CharUnicodeInfo.h"


namespace BFX
{
REFUTF32Encoding::REFUTF32Encoding(bool bBigEndian)
		: REFEncoding(bBigEndian ? 12001 : 12000)
{
	m_bBigEndian = bBigEndian;
}

REFUTF32Encoding::~REFUTF32Encoding()
{
}

int REFUTF32Encoding::GetMaxByteCount(int nCharCount) const
{
	BFX_REQUIRE1 (nCharCount >= 0, "Non-negative number required.");

	// Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
	long long nByteCount = (long long)nCharCount + 1;


	// 4 bytes per char
	nByteCount *= 4;

	BFX_ENSURE1 (nByteCount <= 0x7fffffff, "Too many characters. The resulting number of bytes is larger than what can be returned as an int.");

	return (int)nByteCount;
}

int REFUTF32Encoding::GetMaxCharCount(int nByteCount) const
{
	BFX_REQUIRE1 (nByteCount >= 0, "Non-negative number required.");

	// A supplementary character becomes 2 surrogate characters, so 4 input bytes becomes 2 chars,
	// plus we may have 1 surrogate char left over if the decoder has 3 bytes in it already for a non-bmp char.
	// Have to add another one because 1/2 == 0, but 3 bytes left over could be 2 char surrogate pair
	int nCharCount = (nByteCount / 2) + 2;

	BFX_ENSURE1 (nCharCount <= 0x7fffffff, "Too many characters. The resulting number of bytes is larger than what can be returned as an int.");

	return nCharCount;
}

int REFUTF32Encoding::GetByteCount(const UCHAR* pChars, int nCount) const
{
	BFX_ASSERT(pChars!=NULL);
	BFX_ASSERT(nCount >=0);

	const UCHAR* pEnd = pChars + nCount;
	const UCHAR* pCharStart = pChars;
	int nByteCount = 0;

	UCHAR highSurrogate = '\0';

	bool bFallingBack = false;

	UCHAR ch; 
TryAgain:

	while (((ch = bFallingBack ? '?' : 0) != 0) || pChars < pEnd)
	{ 
		bFallingBack = false;

		// First unwind any fallback
		if (ch == 0) 
		{ 
			// No fallback, just get next char
			ch = *pChars; 
			pChars++;
		}

		// Do we need a low surrogate? 
		if (highSurrogate != '\0')
		{ 
			// 
			// In previous char, we encounter a high surrogate, so we are expecting a low surrogate here.
			// 
			if (CharUnicodeInfo::IsLowSurrogate(ch))
			{
				// They're all legal
				highSurrogate = '\0';

				// 
				// One surrogate pair will be translated into 4 bytes UTF32. 
				//

				nByteCount += 4;
				continue;
			}

			// We are missing our low surrogate, decrement chars and fallback the high surrogate
			// The high surrogate may have come from the encoder, but nothing else did. 
			BFX_ASSERT(pChars > pCharStart);	// Expected chars to have advanced if no low surrogate
			pChars--; 

			// Do the fallback
			bFallingBack = true;
			pChars++;

			// We're going to fallback the old high surrogate.
			highSurrogate = '\0'; 
			continue; 

		} 

		// Do we have another high surrogate?
		if (CharUnicodeInfo::IsHighSurrogate(ch))
		{ 
			//
			// We'll have a high surrogate to check next time. 
			// 
			highSurrogate = ch;
			continue; 
		}

		// Check for illegal characters
		if (CharUnicodeInfo::IsLowSurrogate(ch)) 
		{
			// We have a leading low surrogate, do the fallback 
			bFallingBack = true;
			pChars++;

			// Try again with fallback buffer 
			continue;
		}

		// We get to add the character (4 bytes UTF32) 
		nByteCount += 4;
	} 

	// May have to do our last surrogate
	if (highSurrogate > 0) 
	{
		// We have to do the fallback for the lonely high surrogate
		bFallingBack = true;
		pChars++;

		highSurrogate = (UCHAR)0; 
		goto TryAgain;
	} 

	// Check for overflows.
	BFX_ENSURE1 (nByteCount >= 0, "Too many characters. The resulting number of bytes is larger than what can be returned as an int.");

	// Return our count
	return nByteCount;
}

int REFUTF32Encoding::GetBytes(const UCHAR* pChars, int nCharCount, OUT BYTE* pBytes, int nByteCount) const
{
	BFX_ASSERT(pChars!=NULL); 
	BFX_ASSERT(pBytes!=NULL);
	BFX_ASSERT(nByteCount >=0);
	BFX_ASSERT(nCharCount >=0);

	const UCHAR* pCharStart = pChars;
	const UCHAR* pCharEnd = pChars + nCharCount;
	BYTE* pByteStart = pBytes;
	BYTE* pByteEnd = pBytes + nByteCount;

	UCHAR highSurrogate = '\0';

	bool bFallingBack = false;

	UCHAR ch;
TryAgain: 

	while (((ch = bFallingBack ? '?' : 0) != 0) || pChars < pCharEnd) 
	{
		bFallingBack = false;

		// First unwind any fallback
		if (ch == 0) 
		{
			// No fallback, just get next char
			ch = *pChars;
			pChars++; 
		}

		// Do we need a low surrogate? 
		if (highSurrogate != '\0')
		{ 
			//
			// In previous char, we encountered a high surrogate, so we are expecting a low surrogate here.
			//
			if (CharUnicodeInfo::IsLowSurrogate(ch)) 
			{
				// Is it a legal one? 
				UINT iTemp = GetSurrogate(highSurrogate, ch);
				highSurrogate = '\0';

				//
				// One surrogate pair will be translated into 4 bytes UTF32.
				//
				if (pBytes+3 >= pByteEnd)
				{
					// Don't have 4 bytes 
					BFX_ASSERT (!bFallingBack);

					// If we don't have enough room, then either we should've advanced a while
					// or we should have bytes==byteStart and throw below 
					BFX_ASSERT1((pChars > pCharStart + 1 || pBytes == pByteStart), "Expected chars to have when no room to add surrogate pair");
					pChars-=2;                                      // Aren't using those 2 chars 

					// Throw maybe (if no bytes written)
					BFX_ENSURE1 (pBytes != pByteStart, "The output byte buffer is too small to contain the encoded data");
					highSurrogate = (UCHAR)0;                         // Nothing left over (we backed up to start of pair if supplimentary)
					break;
				}

				if (m_bBigEndian) 
				{
					*(pBytes++) = (BYTE)(0x00); 
					*(pBytes++) = (BYTE)(iTemp >> 16);       // Implies & 0xFF, which isn't needed cause high are all 0
					*(pBytes++) = (BYTE)(iTemp >> 8);        // Implies & 0xFF
					*(pBytes++) = (BYTE)(iTemp);             // Implies & 0xFF
				} 
				else
				{ 
					*(pBytes++) = (BYTE)(iTemp);             // Implies & 0xFF 
					*(pBytes++) = (BYTE)(iTemp >> 8);        // Implies & 0xFF
					*(pBytes++) = (BYTE)(iTemp >> 16);       // Implies & 0xFF, which isn't needed cause high are all 0 
					*(pBytes++) = (BYTE)(0x00);
				}
				continue;
			} 

			// We are missing our low surrogate, decrement chars and fallback the high surrogate 
			// The high surrogate may have come from the encoder, but nothing else did. 
			BFX_ASSERT1(pChars > pCharStart, "Expected chars to have advanced if no low surrogate");
			// pChars--;

			// Do the fallback
			bFallingBack = true;
			pChars ++;

			// We're going to fallback the old high surrogate. 
			highSurrogate = '\0';
			continue;
		} 

		// Do we have another high surrogate?, if so remember it
		if (CharUnicodeInfo::IsHighSurrogate(ch))
		{ 
			//
			// We'll have a high surrogate to check next time. 
			// 
			highSurrogate = ch;
			continue; 
		}

		// Check for illegal characters (low surrogate)
		if (CharUnicodeInfo::IsLowSurrogate(ch)) 
		{
			// We have a leading low surrogate, do the fallback 
			bFallingBack = true;
			pChars ++;

			// Try again with fallback buffer 
			continue;
		}

		// We get to add the character, yippee. 
		if (pBytes+3 >= pByteEnd)
		{ 
			// Don't have 4 bytes 
			BFX_ASSERT (!bFallingBack);

			// Must've advanced already
			BFX_ASSERT(pChars > pCharStart);	// Expected chars to have advanced if normal character");
			pChars--;                                       // Aren't using this char 

			// Throw maybe (if no bytes written)
			BFX_ENSURE(pBytes == pByteStart);
			break;                                              // Didn't throw, stop 
		}

		if (m_bBigEndian)
		{ 
			*(pBytes++) = (BYTE)(0x00);
			*(pBytes++) = (BYTE)(0x00); 
			*(pBytes++) = (BYTE)((UINT)ch >> 8); // Implies & 0xFF
			*(pBytes++) = (BYTE)(ch);            // Implies & 0xFF
		} 
		else
		{
			*(pBytes++) = (BYTE)(ch);            // Implies & 0xFF
			*(pBytes++) = (BYTE)((UINT)ch >> 8); // Implies & 0xFF
			*(pBytes++) = (BYTE)(0x00);
			*(pBytes++) = (BYTE)(0x00); 
		} 
	}

	// May have to do our last surrogate
	if (highSurrogate > 0)
	{
		// We have to do the fallback for the lonely high surrogate
		bFallingBack = true;
		pChars ++;

		highSurrogate = (UCHAR)0; 
		goto TryAgain; 
	}

	// Fix our encoder if we have one
	BFX_ASSERT(highSurrogate == 0);	// Expected encoder to be flushed.

	// return the new length
	return (int)(pBytes - pByteStart);
}

int REFUTF32Encoding::GetCharCount(const BYTE* pBytes, int nCount, REFDecoder* pDecoder) const
{
	BFX_ASSERT(pBytes!=NULL);
	BFX_ASSERT(nCount >=0);

	UTF32Decoder* pUtf32Decoder = (UTF32Decoder*)pDecoder;

	// None so far! 
	int nCharCount = 0;
	const BYTE* pEnd = pBytes + nCount; 
	const BYTE* pByteStart = pBytes;

	// Set up decoder
	int nReadCount = 0; 
	UINT iChar = 0;

	// For fallback we may need a fallback buffer
	REF<REFDecoderFallbackBuffer> pFallbackBuffer;

	// See if there's anything in our decoder
	if (pUtf32Decoder != NULL)
	{
		nReadCount = pUtf32Decoder->m_nReadByteCount;
		iChar = (UINT32)pUtf32Decoder->m_iChar;
		pFallbackBuffer = pUtf32Decoder->GetFallbackBuffer();

		// Shouldn't have anything in fallback buffer for GetCharCount
		BFX_ASSERT1(pFallbackBuffer->GetRemaining() == 0, "Expected empty fallback buffer at start");
	}
	else
	{
		pFallbackBuffer = GetDecoderFallback()->CreateFallbackBuffer();
	}

	// Set our internal fallback interesting things.
	pFallbackBuffer->Initialize(pByteStart, NULL);

	// Loop through our input, 4 characters at a time!
	while (pBytes < pEnd && nCharCount >= 0)
	{
		// Get our next character 
		if(m_bBigEndian) 
		{
			// Scoot left and add it to the bottom 
			iChar <<= 8;
			iChar += *(pBytes++);
		}
		else 
		{
			// Scoot right and add it to the top 
			iChar >>= 8; 
			iChar += (UINT)(*(pBytes++)) << 24;
		} 

		nReadCount++;

		// See if we have all the bytes yet 
		if (nReadCount < 4)
			continue; 

		// Have the bytes
		nReadCount = 0; 

		// See if its valid to encode
		if (iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF))
		{ 
			// Need to fall back these 4 bytes
			SequenceT<BYTE> bytesUnknown;
			if (m_bBigEndian)
			{
				bytesUnknown.Append((BYTE)(iChar>>24));
				bytesUnknown.Append((BYTE)(iChar>>16));
				bytesUnknown.Append((BYTE)(iChar>>8));
				bytesUnknown.Append((BYTE)(iChar));
			}
			else
			{
				bytesUnknown.Append((BYTE)(iChar));
				bytesUnknown.Append((BYTE)(iChar>>8));
				bytesUnknown.Append((BYTE)(iChar>>16));
				bytesUnknown.Append((BYTE)(iChar>>24));
			}

			nCharCount += pFallbackBuffer->Fallback(bytesUnknown, pBytes);

			// Ignore the illegal character
			iChar = 0;
			continue;
		} 

		// Ok, we have something we can add to our output 
		if (iChar >= 0x10000)
		{
			// Surrogates take 2
			nCharCount++;
		}

		// Add the rest of the surrogate or our normal character 
		nCharCount++;

		// iChar is back to 0
		iChar = 0;
	}

	// See if we have something left over that has to be decoded
	if (nReadCount > 0) 
	{ 
		// Oops, there's something left over with no place to go.
		SequenceT<BYTE> bytesUnknown;
		int nBufferSize = nReadCount;
		BYTE* pBuf = bytesUnknown.GetBuffer(nBufferSize);
		if (m_bBigEndian)
		{
			while(nReadCount > 0)
			{
				pBuf[--nReadCount] = (BYTE)iChar;
				iChar >>= 8;
			}
		}
		else
		{
			while (nReadCount > 0)
			{
				pBuf[--nReadCount] = (BYTE)(iChar >> 24);
				iChar <<= 8;
			}
		}
		bytesUnknown.ReleaseBuffer(nBufferSize);

		nReadCount += pFallbackBuffer->Fallback(bytesUnknown, pBytes);
	}

	// Check for overflows.
	BFX_ENSURE1 (nCharCount >= 0, "Too many characters. The resulting number of bytes is larger than what can be returned as an int.");

	// Shouldn't have anything in fallback buffer for GetCharCount
	// (don't have to check m_throwOnOverflow for chars or count)
	BFX_ASSERT1(pFallbackBuffer->GetRemaining() == 0, "Expected empty fallback buffer at end");

	// Return our count
	return nCharCount;
}

int REFUTF32Encoding::GetChars(const BYTE* pBytes, int nByteCount, OUT UCHAR* pChars, int nCharCount, REFDecoder* pDecoder) const
{
	BFX_ASSERT(pChars!=NULL);
	BFX_ASSERT(pBytes!=NULL);
	BFX_ASSERT(nByteCount >=0);
	BFX_ASSERT(nCharCount >=0);

	UTF32Decoder* pUtf32Decoder = (UTF32Decoder*)pDecoder;

	// None so far!
	UCHAR* pCharStart = pChars; 
	UCHAR* pCharEnd = pChars + nCharCount;

	const BYTE* pByteStart = pBytes;
	const BYTE* pByteEnd = pBytes + nByteCount;

	// See if there's anything in our decoder (but don't clear it yet) 
	int nReadCount = 0; 
	UINT iChar = 0;

	// For fallback we may need a fallback buffer
	REF<REFDecoderFallbackBuffer> pFallbackBuffer;

	// See if there's anything in our decoder
	if (pUtf32Decoder != NULL)
	{
		nReadCount = pUtf32Decoder->m_nReadByteCount;
		iChar = (UINT32)pUtf32Decoder->m_iChar;
		pFallbackBuffer = pUtf32Decoder->GetFallbackBuffer();

		// Shouldn't have anything in fallback buffer for GetChars
		BFX_ASSERT1(pFallbackBuffer->GetRemaining() == 0, "Expected empty fallback buffer at start");
	}
	else
	{
		pFallbackBuffer = GetDecoderFallback()->CreateFallbackBuffer();
	}

	// Set our internal fallback interesting things.
	pFallbackBuffer->Initialize(pBytes, pChars + nCharCount);

	// Loop through our input, 4 characters at a time! 
	while (pBytes < pByteEnd) 
	{
		// Get our next character 
		if(m_bBigEndian)
		{
			// Scoot left and add it to the bottom
			iChar <<= 8; 
			iChar += *(pBytes++);
		} 
		else 
		{
			// Scoot right and add it to the top 
			iChar >>= 8;
			iChar += (UINT)(*(pBytes++)) << 24;
		}

		nReadCount++;

		// See if we have all the bytes yet 
		if (nReadCount < 4)
			continue; 

		// Have the bytes
		nReadCount = 0;

		// See if its valid to encode
		if ( iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF)) 
		{ 
			// Need to fall back these 4 bytes
			SequenceT<BYTE> bytesUnknown;
			if (m_bBigEndian)
			{
				bytesUnknown.Append((BYTE)(iChar>>24));
				bytesUnknown.Append((BYTE)(iChar>>16));
				bytesUnknown.Append((BYTE)(iChar>>8));
				bytesUnknown.Append((BYTE)(iChar));
			}
			else
			{
				bytesUnknown.Append((BYTE)(iChar));
				bytesUnknown.Append((BYTE)(iChar>>8));
				bytesUnknown.Append((BYTE)(iChar>>16));
				bytesUnknown.Append((BYTE)(iChar>>24));
			}

			// Chars won't be updated unless this works.
			if (!pFallbackBuffer->Fallback(bytesUnknown, pBytes, INOUT pChars))
			{
				// Couldn't fallback, throw or wait til next time
				// We either read enough bytes for bytes-=4 to work, or we're
				// going to throw in ThrowCharsOverflow because chars == charStart
				BFX_ASSERT1(pBytes >= pByteStart + 4 || pChars == pCharStart, "Expected to have consumed bytes or throw (bad surrogate)");
				pBytes -= 4;									// get back to where we were
				iChar = 0;										// Remembering nothing
				pFallbackBuffer->ResetAll();
				BFX_ENSURE(pChars != pCharStart);				// Might throw, if no chars output
				break;											// Stop here, didn't throw
			}

			// Ignore the illegal character
			iChar = 0;
			continue;
		}


		// Ok, we have something we can add to our output
		if (iChar >= 0x10000) 
		{
			// Surrogates take 2
			if (pChars >= pCharEnd - 1)
			{ 
				// Throwing or stopping
				// We either read enough bytes for bytes-=4 to work, or we're 
				// going to throw because chars == charStart 
				BFX_ASSERT(pBytes >= pByteStart + 4 || pChars == pCharStart);	// Expected to have consumed bytes or throw (surrogate)
				pBytes-=4;                                      // get back to where we were
				iChar=0;                                        // Remembering nothing

				// Might throw, if no chars output
				BFX_ENSURE(pChars != pCharStart);
				break;                                          // Stop here, didn't throw 
			}

			*(pChars++) = GetHighSurrogate(iChar); 
			iChar = GetLowSurrogate(iChar);
		} 
		// Bounds check for normal character
		else if (pChars >= pCharEnd)
		{
			// Throwing or stopping 
			// We either read enough bytes for bytes-=4 to work, or we're
			// going to throw in ThrowCharsOverflow because chars == charStart 
			BFX_ASSERT(pBytes >= pByteStart + 4 || pChars == pCharStart);	// Expected to have consumed bytes or throw (normal char)
			pBytes-=4;                                      // get back to where we were 
			iChar=0;                                        // Remembering nothing

			// Might throw, if no chars output
			BFX_ENSURE(pChars != pCharStart);
			break;                                          // Stop here, didn't throw
		} 

		// Add the rest of the surrogate or our normal character 
		*(pChars++) = (UCHAR)iChar; 

		// iChar is back to 0 
		iChar = 0;
	}

	// See if we have something left over that has to be decoded 
	if (nReadCount > 0)
	{ 
		// Oops, there's something left over with no place to go.
		SequenceT<BYTE> bytesUnknown;
		BYTE* pBuf = bytesUnknown.GetBuffer(nReadCount);
		int nTempCount = nReadCount;
		if (m_bBigEndian)
		{
			while(nTempCount > 0)
			{
				pBuf[--nTempCount] = (BYTE)iChar;
				iChar >>= 8;
			}
		}
		else
		{
			while (nTempCount > 0)
			{
				pBuf[--nTempCount] = (BYTE)(iChar>>24);
				iChar <<= 8;
			}
		}
		bytesUnknown.ReleaseBuffer(nReadCount);

		if (!pFallbackBuffer->Fallback(bytesUnknown, pBytes, INOUT pChars))
		{
			// Couldn't fallback.
			pFallbackBuffer->ResetAll();
			BFX_ENSURE(pChars != pCharStart);	// Might throw, if no chars output
			// Stop here, didn't throw, backed up, so still nothing in buffer
		}
		else
		{
			// Don't clear our decoder unless we could fall it back.
			// If we caught the if above, then we're a convert() and will catch this next time.
			nReadCount = 0;
			iChar = 0;
		}
	}

	// Remember any left over stuff, clearing buffer as well for MustFlush
	if (pUtf32Decoder != NULL)
	{
		pUtf32Decoder->m_iChar = (int)iChar;
		pUtf32Decoder->m_nReadByteCount = nReadCount;
		// TODO: pUtf32Decoder->m_nBytesUsed = (int)(pBytes - pByteStart);
	}

	// Shouldn't have anything in fallback buffer for GetChars
	// (don't have to check m_throwOnOverflow for chars)
	BFX_ASSERT1(pFallbackBuffer->GetRemaining() == 0, "Expected empty fallback buffer at end");

	// Return our count
	return (int)(pChars - pCharStart);
}

// Returns a Decoder object for this encoding. The returned object can be used to decode a sequence of bytes into a sequence of characters.
// Contrary to the GetChars family of methods, a Decoder can convert partial sequences of bytes into partial sequences of characters
// by maintaining the appropriate state between the conversions.
REFDecoder* REFUTF32Encoding::GetDecoder() const
{
	REFDecoder* pDecoder = new UTF32Decoder(this);
	pDecoder->Autorelease();
	return pDecoder;
}

REFUTF32Encoding::UTF32Decoder::UTF32Decoder(const REFEncoding* pEncoding)
	: REFDecoder(pEncoding)
{
}
REFUTF32Encoding::UTF32Decoder::~UTF32Decoder()
{
}
void REFUTF32Encoding::UTF32Decoder::Reset()
{
	m_iChar = 0;
	m_nReadByteCount = 0;
	REFDecoder::Reset();
}

}	//	namespace BFX
